﻿using huangyufan.OpenDataService.Context;
using Microsoft.AspNetCore.Builder;
using Microsoft.AspNetCore.Http;
using Microsoft.AspNetCore.Routing;
using Microsoft.AspNetCore.Routing.Template;
using Microsoft.Extensions.Configuration;
using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.Options;
using Microsoft.OpenApi.Models;
using Microsoft.OpenApi.Writers;
using Swashbuckle.AspNetCore.Swagger;
using System;
using System.Collections.Generic;
using System.Data;
using System.Globalization;
using System.IO;
using System.Text;
using System.Threading.Tasks;

namespace huangyufan.OpenDataService.Middleware
{
    public class CustomSwaggerMiddleware
    {
        private readonly RequestDelegate _next;
        private readonly SwaggerOptions _options;
        private readonly TemplateMatcher _requestMatcher;

        private IConfiguration Configuration;

        public CustomSwaggerMiddleware(
            RequestDelegate next,
            SwaggerOptions options, IConfiguration configuration)
        {
            _next = next;
            _options = options ?? new SwaggerOptions();
            _requestMatcher = new TemplateMatcher(TemplateParser.Parse(_options.RouteTemplate), new RouteValueDictionary());
            Configuration = configuration;
        }

        public async Task Invoke(HttpContext httpContext, ISwaggerProvider swaggerProvider)
        {
            if (!RequestingSwaggerDocument(httpContext.Request, out string documentName))
            {
                await _next(httpContext);
                return;
            }

            try
            {
                var basePath = httpContext.Request.PathBase.HasValue
                    ? httpContext.Request.PathBase.Value
                    : null;

                var swagger = swaggerProvider.GetSwagger(
                    documentName: documentName,
                    host: null,
                    basePath: basePath);

                string str=Newtonsoft.Json.JsonConvert.SerializeObject(swagger.Paths);

                AddCustomOpenApiPath(swagger.Paths);

                // One last opportunity to modify the Swagger Document - this time with request context
                foreach (var filter in _options.PreSerializeFilters)
                {
                    filter(swagger, httpContext.Request);
                }

                if (Path.GetExtension(httpContext.Request.Path.Value) == ".yaml")
                {
                    await RespondWithSwaggerYaml(httpContext.Response, swagger);
                }
                else
                {
                    await RespondWithSwaggerJson(httpContext.Response, swagger);
                }
            }
            catch (UnknownSwaggerDocument)
            {
                RespondWithNotFound(httpContext.Response);
            }
        }

        private bool RequestingSwaggerDocument(HttpRequest request, out string documentName)
        {
            documentName = null;
            if (request.Method != "GET") return false;

            var routeValues = new RouteValueDictionary();
            if (!_requestMatcher.TryMatch(request.Path, routeValues) || !routeValues.ContainsKey("documentName")) return false;

            documentName = routeValues["documentName"].ToString();
            return true;
        }

        private void RespondWithNotFound(HttpResponse response)
        {
            response.StatusCode = 404;
        }

        private async Task RespondWithSwaggerJson(HttpResponse response, OpenApiDocument swagger)
        {
            response.StatusCode = 200;
            response.ContentType = "application/json;charset=utf-8";

            using (var textWriter = new StringWriter(CultureInfo.InvariantCulture))
            {
                var jsonWriter = new OpenApiJsonWriter(textWriter);
                if (_options.SerializeAsV2) swagger.SerializeAsV2(jsonWriter); else swagger.SerializeAsV3(jsonWriter);

                await response.WriteAsync(textWriter.ToString(), new UTF8Encoding(false));
            }
        }

        private async Task RespondWithSwaggerYaml(HttpResponse response, OpenApiDocument swagger)
        {
            response.StatusCode = 200;
            response.ContentType = "text/yaml;charset=utf-8";

            using (var textWriter = new StringWriter(CultureInfo.InvariantCulture))
            {
                var yamlWriter = new OpenApiYamlWriter(textWriter);
                if (_options.SerializeAsV2) swagger.SerializeAsV2(yamlWriter); else swagger.SerializeAsV3(yamlWriter);

                await response.WriteAsync(textWriter.ToString(), new UTF8Encoding(false));
            }
        }

        /// <summary>
        /// 添加自定义api文档
        /// </summary>
        /// <param name="keyValuePairs"></param>
        private void AddCustomOpenApiPath(OpenApiPaths keyValuePairs)
        {
            string sqlStr = "select object_id,name from sys.all_objects where type='V' and is_ms_shipped=0 and name like 'ODV_%'";
            var dataTable=SqlHelper.ExcuteDataTable(Configuration.GetConnectionString("DefaultConnection"), sqlStr);

            foreach (DataRow item in dataTable.Rows)
            {
                string sqlColumnsStr = $"select name from sys.all_columns where object_id = {item[0]} order by column_id";
                var dataColumns = SqlHelper.ExcuteDataTable(Configuration.GetConnectionString("DefaultConnection"), sqlColumnsStr);

                //api参数
                List<OpenApiParameter> openApiParameters = new List<OpenApiParameter>();
                foreach (DataRow colPara in dataColumns.Rows)
                {
                    openApiParameters.Add(new OpenApiParameter() { Name = colPara[0].ToString() });
                };

                //api相应信息
                OpenApiResponses openApiResponses = null;

                //api信息
                OpenApiOperation openApiOperation = new OpenApiOperation
                {
                    Tags = new List<OpenApiTag>() { new OpenApiTag()
                    {
                        Name =item[1].ToString().Remove(0,4).ToLower(),
                        UnresolvedReference =false
                    } },
                    OperationId = item[1].ToString().Remove(0, 4).ToLower(),
                    Parameters = openApiParameters,
                    RequestBody = null,
                    Responses = openApiResponses,
                    Deprecated = false
                };
                Dictionary<OperationType, OpenApiOperation> dic = new Dictionary<OperationType, OpenApiOperation>();
                dic.Add(OperationType.Get, openApiOperation);
                OpenApiPathItem openApiPathItem = new OpenApiPathItem();
                openApiPathItem.Operations = dic;

                keyValuePairs.Add($"/{item[1].ToString().Remove(0, 4).ToLower()}",
                    openApiPathItem);

            }

            
        }
    }

    public static class CustomSwaggerMiddlewareEctensions
    {
        /// <summary>
        /// Register the Swagger middleware with provided options
        /// </summary>
        public static IApplicationBuilder UseCustomSwagger(this IApplicationBuilder app, SwaggerOptions options)
        {
            return app.UseMiddleware<CustomSwaggerMiddleware>(options);
        }

        /// <summary>
        /// Register the Swagger middleware with optional setup action for DI-injected options
        /// </summary>
        public static IApplicationBuilder UseCustomSwagger(
            this IApplicationBuilder app,
            Action<SwaggerOptions> setupAction = null)
        {
            SwaggerOptions options;
            using (var scope = app.ApplicationServices.CreateScope())
            {
                options = scope.ServiceProvider.GetRequiredService<IOptionsSnapshot<SwaggerOptions>>().Value;
                setupAction?.Invoke(options);
            }

            return app.UseCustomSwagger(options);
        }
    }
}
